package engine.utils
{
	import flash.geom.Point;
	
	import org.flintparticles.common.utils.Maths;
	
	public class MathUtils
	{
		/**
		 * The constant Number value of Math.PI x 2, or roughly 6.283185307179586 radians.
		 */
		public static const PI2:Number = Math.PI * 2;
		
		public static const DEG_TO_RAD:Number = Math.PI / 180;
		public static const RAD_TO_DEG:Number = 180 / Math.PI;
		
		/**
		 * Converts <code>degrees</code> to an angle in radians.
		 * 
		 * @param Degrees: The degrees to convert.
		 * @return An angle in radians.
		 */
		public static function toRadians(degrees:Number):Number
		{
			return degrees * DEG_TO_RAD;
		}
		
		/**
		 * Converts <code>radians</code> to an angle in degrees.
		 * 
		 * @param Radians: The radians to convert.
		 * @return An angle in degrees.
		 */
		public static function toDegrees(radians:Number):Number
		{
			return radians * RAD_TO_DEG;
		}
		
		/**
		 * Adjusts <code>rotation</code> to only be between +/-180 or +/-PI. Actionscript
		 * constrains its rotations, so all rotations should do the same.
		 * 
		 * @param Rotation: The rotation to adjust.
		 * @param IsDegrees: If true, <code>rotation</code> is in degrees, and will be returned
		 * between +/-180. If false, <code>rotation</code> is in radians, and will be return between +/-PI.
		 * @return The adjusted rotation.
		 */
		public static function toRotation(rotation:Number, isDegrees:Boolean = true):Number
		{
			if(isDegrees)
			{
				if(Math.abs(rotation) > 180)	
				{
					if(rotation > 180) 			{ while(rotation > 180) 	rotation -= 360; }
					else if(rotation < -180) 	{ while(rotation < -180) 	rotation += 360; }
				}
			}
			else
			{
				if(Math.abs(rotation) > Math.PI)	
				{
					if(rotation > Math.PI) 			{ while(rotation > Math.PI) 	rotation -= MathUtils.PI2; }
					else if(rotation < -Math.PI) 	{ while(rotation < -Math.PI) 	rotation += MathUtils.PI2; }
				}
			}
			return rotation;
		}
		
		public static function toPercent(value:Number, min:Number, max:Number, limit:Boolean = true):Number
		{
			var percent:Number = (value - min) / (max - min);
			
			if(limit)
			{
				if(percent > 1) 		percent = 1;
				else if(percent < 0) 	percent = 0;
			}
			
			return percent;
		}
		
		/**
		 * Calculates the angle in degrees between a current and target coordinate
		 * represented by <code>(x1, y1)</code> and <code>(x2, y2)</code>, respectively.
		 * 
		 * @param X1: The X value of the 1st coordinate.
		 * @param Y1: The Y value of the 1st coordinate.
		 * @param X2: The X value of the 2nd coordinate.
		 * @param Y2: The Y value of the 2nd coordinate.
		 * @return The angle in degrees between the 2 coordinates.
		 */
		public static function degreesBetween(x1:Number, y1:Number, x2:Number, y2:Number):Number
		{
			return MathUtils.toDegrees(Math.atan2(y2 - y1, x2 - x1));
		}
		
		/**
		 * Calculates the angle in radians between a current and target coordinate
		 * represented by <code>(x1, y1)</code> and <code>(x2, y2)</code>, respectively.
		 * 
		 * @param X1: The X value of the 1st coordinate.
		 * @param Y1: The Y value of the 1st coordinate.
		 * @param X2: The X value of the 2nd coordinate.
		 * @param Y2: The Y value of the 2nd coordinate.
		 * @return The angle in radians between the 2 coordinates.
		 */
		public static function radiansBetween(x1:Number, y1:Number, x2:Number, y2:Number):Number
		{
			return Math.atan2(y2 - y1, x2 - x1);
		}
		
		/**
		 * Generates a random Integer within the range of <code>min</code> and <code>max</code>.
		 * 
		 * @param Min: The minimum Integer value.
		 * @param Max: The maximum Integer value.
		 * @return A random Integer.
		 */
		public static function randomInteger(min:int, max:int):int
		{
			return Math.round(Math.random() * (max - min)) + min;
		}
		
		/**
		 * Generates a random Number within the range of <code>min</code> and <code>max</code>.
		 * 
		 * @param Min: The minimum Number value.
		 * @param Max: The maximum Number value.
		 * @return A random Number.
		 */
		public static function randomNumber(min:Number, max:Number):Number
		{
			return (Math.random() * (max - min)) + min;
		}
		
		/**
		 * Determines whether <code>number</code> is a multiple of <code>multiple</code>.
		 * 
		 * @param Number: The Number to check.
		 * @param Multiple: The Number that <code>number</code> is being checked against.
		 * @return Returns true if <code>number</code> is a multiple.
		 * Returns false if <code>number</code> isn't a multiple.
		 */
		public static function isMultiple(number:Number, multiple:Number):Boolean
		{
			return !(number % multiple);
		}
		
		/**
		 * Multiplies <code>number</code> by a rounded factor that gets it as close to
		 * <code>multiple</code> as possible.
		 * 
		 * @param Number: The Number to multiply by a factor.
		 * @param Multiple: The Number to round to.
		 * @return A multiple of <code>number</code> that's the closest to <code>multiple<code>.
		 */
		public static function closestMultiple(number:Number, multiple:Number):Number
		{
			return Math.round(multiple / number) * number;
		}
		
		/**
		 * Limits the range of <code>number</code> to be inside/outside the range of <code>min</code> and
		 * <code>max</code>. The value of <code>inside</code> determines whether to limit
		 * <code>number</code> inside or outside the bounds of <code>min</code> and <code>max</code>.
		 * 
		 * @param Number: The Number to limit.
		 * @param Min: The minimum Number value.
		 * @param Max: The maximum Number value.
		 * @param Inside: If true, limits <code>number</code> inside the range. If false,
		 * limits <code>number</code> outside the range.
		 * @return The limited Number.
		 */
		public static function limitRange(number:Number, min:Number, max:Number, inside:Boolean = true):Number
		{
			if(inside)
			{
				if(number < min) 			number = min;
				else if(number > max) 		number = max;
			}
			else
			{
				if(number > min && number < max)
				{
					var toMin:Number = Math.abs(number - min);
					var toMax:Number = Math.abs(number - max);
					
					if(toMin < toMax) number = min;
					else number = max;
				}
			}
			
			return number;
		}
		
		/**
		 * Determines whether a given Number is closer to <code>value1</code> or <code>value2</code>.
		 * 
		 * @param Number: The Number to check.
		 * @param Value1: The 1st value to check against.
		 * @param Value2: The 2nd value to check against.
		 * @return The value closest to <code>number</code>.
		 */
		public static function closestNumber(number:Number, value1:Number, value2:Number):Number
		{
			return (Math.abs(number - value1) < Math.abs(number - value2)) ? value1 : value2;
		}
	}
}